package com.service.business.member.service.impl;

import com.google.common.collect.Lists;
import com.service.business.base.service.ConfigService;
import com.service.business.framework.log.TimeLog;
import com.service.business.member.dto.MemberStoreChange;
import com.service.business.member.service.*;
import com.service.business.order.service.OrderProductOtherService;
import com.service.business.pay.dto.PayOrder;
import com.service.business.support.MemberStoreLockUtils;
import com.service.dao.constants.ConfigConstants;
import com.service.dao.enums.CoinsEnum;
import com.service.dao.enums.GradeEnum;
import com.service.dao.enums.MemberJAType;
import com.service.dao.model.*;
import com.service.dao.po.MemberCache;
import com.service.utility.common.MathUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * <dl>
 * <dt>back-end</dt>
 * <dd>Description:</dd>
 * <dd>Copyright: Copyright (C) 2015</dd>
 * <dd>Company: </dd>
 * <dd>CreateDate: 2019年12月27日</dd>
 * </dl>
 *
 * @author
 */
@Slf4j
@Service
public class MemberAwardServiceImpl implements MemberAwardService {

    @Autowired
    private MemberService memberService;
    @Autowired
    private ConfigService configService;
    @Autowired
    private MemberOutService memberOutService;
    @Autowired
    private MemberCacheService memberCacheService;
    @Autowired
    private MemberStoreService memberStoreService;
    @Autowired
    private MemberGradeConfigService gradeConfigService;
    @Autowired
    private OrderProductOtherService orderProductOtherService;
    @Autowired
    private RecommendAwardConfigService recommendAwardConfigService;

    @Autowired
    private MemberStoreLockUtils memberStoreLockUtils;

    private final BigDecimal ONE_HUNDRED = BigDecimal.valueOf(100.00);

    @Override
    @TimeLog("会员支付成功-计算奖金以及自身等级")
    @Transactional(rollbackFor = Exception.class)
    public void dealMemberPayGiftBagOrder(PayOrder payOrder) {
        Member member = memberService.getById(payOrder.getMemberId());
        OrderProductOther orderProductOther = orderProductOtherService.getById(payOrder.getOrderId());
        if (ObjectUtils.isEmpty(member) || ObjectUtils.isEmpty(orderProductOther)) {
            log.error("dealMemberPayGiftBagOrder 处理错误，非法支付数据:{}", payOrder);
            return;
        }
        Map<String, String> configMap = configService.getConfig2MapByGroup(ConfigConstants.Group.award.getCode());
        CoinsEnum coins = getAwardCoins(configMap);
        //修改redis会员缓存
        MemberCache payMemberCache = this.calculateMemberCache(member.getId(), payOrder.getPayMoney(), configMap);
        //设置父级会员直推有效会员列表
        this.addMember2ParentMemberValidDirectList(member, payMemberCache);
        //直接奖,间接奖,自身是有效会员，并且有N个有效直推会员
        this.calculateRecommendAward(member, configMap, payOrder, coins);
        //社区奖与节点奖
        this.calculateCommunityAndNodeAward(member, configMap, payOrder, coins);

        orderProductOtherService.releaseRecommendAward(payOrder.getOrderId());
    }

    @Override
    @TimeLog("计算会员支付订单后相关信息")
    public MemberCache calculateMemberCache(Integer memberId, BigDecimal payMoney, Map<String, String> configMap) {
        MemberCache memberCache = memberCacheService.getMemberCache(memberId);
        memberCache.setTotalConsumption(memberCache.getTotalConsumption().add(payMoney));
        BigDecimal bigDecimal = BigDecimal.valueOf(Long.parseLong(configMap.get(ConfigConstants.VALID_MEMBER_CONSUMPTION)));
        if (!memberCache.getValidMember() && memberCache.getTotalConsumption().compareTo(bigDecimal) >= 0) {
            memberCache.setValidMember(true);
        }
        memberService.checkAndUpdateMemberGrade(memberCache);

        return memberCache;
    }

    @Override
    @TimeLog("设置父级会员直推有效会员列表")
    public void addMember2ParentMemberValidDirectList(Member member, MemberCache payMemberCache) {
        if (ObjectUtils.isEmpty(member.getPid()) || member.getPid() <= 0) {
            log.info("设置父级会员<{}->{}>直推有效会员失败:不存在此会员", member.getPid(), member.getId());
            return;
        }
        MemberCache parentMemberCache = memberCacheService.getMemberCache(member.getPid());
        if (ObjectUtils.isEmpty(parentMemberCache.getValidDRUserIdList())) {
            parentMemberCache.setValidDRUserIdList(Lists.newArrayList());
        }
        if (payMemberCache.getValidMember() && !parentMemberCache.getValidDRUserIdList().contains(member.getId())) {
            parentMemberCache.getValidDRUserIdList().add(member.getId());
            memberCacheService.setMemberCache(parentMemberCache);
        }
    }

    @Override
    @TimeLog("批量计算会员间推奖")
    public void calculateRecommendAward(Member member, Map<String, String> configMap, PayOrder payOrder, CoinsEnum coins) {
        if (StringUtils.isBlank(member.getUserPath())) {
            log.info("计算会员<{}>间推奖失败:该会员为根会员", member.getId());
            return;
        }

        List<Integer> memberPath = getReverseMemberPath(member.getUserPath());
        List<RecommendAwardConfig> awardConfigList = recommendAwardConfigService.listByStatus();
        AtomicInteger count = new AtomicInteger(0);
        BigDecimal multiple = BigDecimal.valueOf(Double.parseDouble(configMap.get(ConfigConstants.TOTAL_CONSUMPTION_MULTIPLE)));

        memberPath.forEach(item -> {
            if (count.get() >= awardConfigList.size()) {
                return;
            }
            RecommendAwardConfig config = awardConfigList.get(count.get());
            MemberCache grandMemberCache = memberCacheService.getMemberCache(item);
            if (grandMemberCache.getValidDRUserIdList().size() >= config.getValidDirectNum()) {
                BigDecimal award = this.calculateMemberAward(grandMemberCache, payOrder, config,
                        getJaType(count.get()), multiple, coins, item + "->" + member.getId() + config.getAwardName(), configMap);
                if (count.get() == 0) {
                    //直推奖 不在限制是否为有效会员可以发放
                    log.info("会员<{}:grade:{}>直推奖:{}", grandMemberCache.getMemberId(), grandMemberCache.getMemberGrade(), award);
                    grandMemberCache.setDirectAward(grandMemberCache.getDirectAward().add(award));
                } else if (grandMemberCache.getValidMember()) {
                    log.info("会员<{}:grade:{}>第{}代间推奖:{}", grandMemberCache.getMemberId(),
                            grandMemberCache.getMemberGrade(), count.get(), award);
                    grandMemberCache.setIndirectAward(grandMemberCache.getIndirectAward().add(award));
                }
                memberCacheService.setMemberCache(grandMemberCache);
                log.info("计算会员<{}->{}>{}成功,奖金:{}", item, config.getAwardName(), member.getId(), award);
            }
            count.getAndIncrement();
        });

    }

    private MemberJAType getJaType(Integer count) {
        return count == 0 ? MemberJAType.RECOMMEND_11 : MemberJAType.RECOMMEND_12;
    }

    @Override
    @TimeLog("计算某个会员间推奖")
    public BigDecimal calculateMemberAward(MemberCache memberCache, PayOrder payOrder,
                                           RecommendAwardConfig config, MemberJAType jaType,
                                           BigDecimal multiple,
                                           CoinsEnum coins, String remark, Map<String, String> sysConfig) {

        BigDecimal residueAwardLimit = calculateResidueAwardLimit(memberCache, multiple);
        //判断剩余奖金额度是否为零，如果是零 则出局
        if (MathUtil.isLEZero(residueAwardLimit)) {
            return BigDecimal.ZERO;
        }
        BigDecimal award = calculateRecommendAward(memberCache, payOrder.getPayMoney(), residueAwardLimit, config.getAwardRate(), sysConfig);
        if (award.compareTo(BigDecimal.ZERO) <= 0) {
            return BigDecimal.ZERO;
        }
        updateMemberStore(memberCache.getMemberId(), coins, payOrder.getOrderId(), award, jaType, remark);

        memberService.checkAndUpdateMemberGrade(memberCache);
        memberCacheService.setMemberCache(memberCache);
        return award;
    }

    public BigDecimal calculateRecommendAward(MemberCache memberCache, BigDecimal payMoney, BigDecimal residueAwardLimit, BigDecimal awardRate, Map<String, String> sysConfig) {
        if (isOpenAwardLimit(sysConfig) && payMoney.compareTo(memberCache.getTotalConsumption()) > 0) {
            payMoney = memberCache.getTotalConsumption();
        }
        BigDecimal award = MathUtil.div(awardRate.multiply(payMoney), ONE_HUNDRED);
        return getLastAward(residueAwardLimit, award);
    }

    private boolean isOpenAwardLimit(Map<String, String> sysConfig) {
        String open = sysConfig.get(ConfigConstants.RECOMMEND_AWARD_LIMIT_OPEN);
        return ObjectUtils.isEmpty(open) || StringUtils.equals(ConfigConstants.Value.ACTIVE.getCode(), open);
    }

    @Override
    @TimeLog("计算会员社区奖与节点奖")
    public void calculateCommunityAndNodeAward(Member member, Map<String, String> configMap, PayOrder payOrder, CoinsEnum coins) {

        if (StringUtils.isBlank(member.getUserPath())) {
            return;
        }
        log.info("会员<{}>推荐路径:{}", member.getId(), member.getUserPath());
        List<Integer> memberPath = getReverseMemberPath(member.getUserPath());
        Map<Integer, MemberGradeConfig> gradeConfigMap = gradeConfigService.list2Map();
        AtomicReference<MemberGradeConfig> lastAwardGrade = new AtomicReference<>();
        lastAwardGrade.set(gradeConfigMap.get(member.getVipGrade()));

        AtomicReference<BigDecimal> lastAward = new AtomicReference<>();
        AtomicReference<BigDecimal> firstLeaderAward = new AtomicReference<>();

        AtomicReference<BigDecimal> leaderAwardRate = new AtomicReference<>(BigDecimal.ZERO);
        if (configMap.containsKey(ConfigConstants.NODE_AWARD_RATE)) {
            leaderAwardRate.set(BigDecimal.valueOf(Double.parseDouble(configMap.get(ConfigConstants.NODE_AWARD_RATE))));
        }
        BigDecimal multiple = BigDecimal.valueOf(Long.parseLong(configMap.get(ConfigConstants.TOTAL_CONSUMPTION_MULTIPLE)));

        memberPath.forEach(item -> {
            MemberCache memberCache = memberCacheService.getMemberCache(item);
            MemberGradeConfig gradeConfig = gradeConfigMap.get(memberCache.getMemberGrade());

            BigDecimal residueAwardLimit = calculateResidueAwardLimit(memberCache, multiple);
            if (MathUtil.isLEZero(residueAwardLimit)) {
                log.info("本次会员<{}:grade:{}>-><{}:grade:{}>社区奖出局", item, gradeConfig.getId(), member.getId(), member.getVipGrade());
                return;
            }
            if (isMemberGradeLessEqualThen(memberCache, lastAwardGrade)) {
                log.info("本次会员<{}:grade:{}>-><{}:grade:{}>社区奖->本次会员等级小于等于上一次发奖的会员等级<grade:{}>",
                        item, gradeConfig.getId(), member.getId(), member.getVipGrade(),
                        ObjectUtils.isEmpty(lastAwardGrade.get()) ? member.getVipGrade() : lastAwardGrade.get().getId());
                if (isTopMemberGrade(memberCache.getMemberGrade()) && !ObjectUtils.isEmpty(lastAward.get())) {
                    calculateNodeAward(memberCache, gradeConfig, member, coins, payOrder, firstLeaderAward, lastAward, leaderAwardRate, residueAwardLimit);
                }
                return;
            }
            if (ObjectUtils.isEmpty(lastAward.get())) {
                BigDecimal award = calculateCommunityAward(memberCache, gradeConfig, member, coins, payOrder, lastAwardGrade, residueAwardLimit);
                lastAward.set(award);
            }
        });
    }

    public List<Integer> getReverseMemberPath(String memberPath) {
        return Lists.reverse(memberService.getMemberPathList(memberPath));
    }

    @Override
    @TimeLog("计算会员社区奖")
    public BigDecimal calculateCommunityAward(MemberCache memberCache, MemberGradeConfig gradeConfig, Member payMember, CoinsEnum coins,
                                              PayOrder payOrder, AtomicReference<MemberGradeConfig> lastAwardGrade,
                                              BigDecimal residueAwardLimit) {
        BigDecimal awardRate = ObjectUtils.isEmpty(lastAwardGrade.get())
                ? gradeConfig.getTeamMemberRate() : MathUtil.sub(gradeConfig.getTeamMemberRate(), lastAwardGrade.get().getTeamMemberRate());

        log.info("本次会员<{}:grade:{}>-><{}:grade:{}>社区奖rate:{}", memberCache.getMemberId(), gradeConfig.getId(), payMember.getId(), payMember.getVipGrade(), awardRate);
        BigDecimal award = MathUtil.div(awardRate.multiply(payOrder.getPayMoney()), ONE_HUNDRED);
        if (!MathUtil.isLEZero(award)) {
            award = getLastAward(residueAwardLimit, award);
            updateMemberStore(memberCache.getMemberId(), coins, payOrder.getOrderId(), award,
                    MemberJAType.RECOMMEND_13, memberCache.getMemberId() + "->" + payMember.getId() + "社区奖");
            memberCache.setCommunityAward(memberCache.getCommunityAward().add(award));
            memberCacheService.setMemberCache(memberCache);
            lastAwardGrade.set(gradeConfig);
        }
        log.info("本次会员<{}:grade:{}> -> <{}:grade:{}>社区奖奖金:{}", memberCache.getMemberId(),
                gradeConfig.getId(), payMember.getId(), payMember.getVipGrade(), award);
        if (isTopMemberGrade(memberCache.getMemberGrade())) {
            return award;
        }
        return null;
    }

    @Override
    @TimeLog("计算会员节点奖")
    public void calculateNodeAward(MemberCache memberCache, MemberGradeConfig gradeConfig, Member payMember,
                                   CoinsEnum coins, PayOrder payOrder, AtomicReference<BigDecimal> firstLeaderAward,
                                   AtomicReference<BigDecimal> lastAward, AtomicReference<BigDecimal> leaderAwardRate,
                                   BigDecimal residueAwardLimit) {
        firstLeaderAward.set(MathUtil.div(lastAward.get().multiply(leaderAwardRate.get()), ONE_HUNDRED));
        log.info("本次会员<{}:grade:{}>-><{}:grade:{}>节点奖:{}", memberCache.getMemberId(), gradeConfig.getId(), payMember.getId(),
                payMember.getVipGrade(), firstLeaderAward.get());

        leaderAwardRate.set(leaderAwardRate.get().compareTo(leaderAwardRate.get()) > 0 ?
                leaderAwardRate.get().subtract(leaderAwardRate.get()) : BigDecimal.ZERO);

        if (firstLeaderAward.get().compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal award = getLastAward(residueAwardLimit, firstLeaderAward.get());
            log.info("本次会员<{}:grade:{}>-><{}:grade:{}>最终节点奖:{}", memberCache.getMemberId(), gradeConfig.getId(), payMember.getId(),
                    payMember.getVipGrade(), award);
            if (MathUtil.isLEZero(award)) {
                return;
            }
            updateMemberStore(memberCache.getMemberId(), coins, payOrder.getOrderId(), award,
                    MemberJAType.RECOMMEND_14, memberCache.getMemberId() + "->" + payMember.getId() + "节点奖");

            memberCache.setNodeAward(memberCache.getNodeAward().add(award));
            memberCacheService.setMemberCache(memberCache);
        }
    }

    private void updateMemberStore(Integer memberId, CoinsEnum coins, Integer orderId, BigDecimal num, MemberJAType jaType, String remark) {
        if (MathUtil.isLEZero(num)) {
            return;
        }

        MemberStore memberStore = memberStoreService.getMemberStore(memberId, coins.getName());
        String key = MemberStoreLockUtils.buildStoreKey(memberId, memberStore.getId());
        try {
            if (memberStoreLockUtils.lock(key)) {
                memberStoreService.changeMemberStore(MemberStoreChange.builder()
                        .memberId(memberId)
                        .coinsId(coins.getCode())
                        .coinsName(coins.getName())
                        .orderId(orderId)
                        .jaType(jaType)
                        .changeNum(num)
                        .remark(remark)
                        .build());
            }
        } finally {
            memberStoreLockUtils.unlock(key);
        }

    }

    private boolean isMemberGradeLessEqualThen(MemberCache memberCache, AtomicReference<MemberGradeConfig> lastGrade) {
        int memberRealGrade, compareRealGrade = 0;
        memberRealGrade = getRealGrade(memberCache.getMemberGrade());
        compareRealGrade = getRealGrade(lastGrade.get().getId());
        return memberRealGrade <= compareRealGrade;
    }

    private int getRealGrade(Integer gradeOne) {
        return gradeOne > 10 ? gradeOne - 10 : gradeOne;
    }

    private boolean isTopMemberGrade(Integer currentGrade) {
        return ObjectUtils.nullSafeEquals(currentGrade, GradeEnum.eight.getValue())
                || ObjectUtils.nullSafeEquals(currentGrade, GradeEnum.eighteen.getValue());
    }


    private BigDecimal getLastAward(BigDecimal residueAwardLimit, BigDecimal award) {

        return residueAwardLimit.compareTo(award) >= 0 ? award : residueAwardLimit;
    }

    private CoinsEnum getAwardCoins(Map<String, String> configMap) {
        CoinsEnum coins = CoinsEnum.USDF;
        if (configMap.containsKey(ConfigConstants.AWARD_COINS_TYPE_ID)) {
            coins = CoinsEnum.valueOf(Integer.parseInt(configMap.get(ConfigConstants.AWARD_COINS_TYPE_ID)));
        }
        return coins;
    }

    public BigDecimal calculateResidueAwardLimit(MemberCache memberCache, BigDecimal multiple) {
        BigDecimal totalAward = memberCacheService.getMemberTotalAward(memberCache);
        BigDecimal multipleResult = memberCache.getTotalConsumption().multiply(multiple);
        if (MathUtil.isLEZero(multipleResult)) {
            log.info("会员<id:{},grade:{}>消费金额为0,可获得奖金余额:0", memberCache.getMemberId(), memberCache.getMemberGrade());
            return BigDecimal.ZERO;
        }
        //剩余奖金额度
        BigDecimal residueAwardLimit = MathUtil.subAndZero(multipleResult, totalAward);
        if (MathUtil.isLEZero(residueAwardLimit)
                && !MathUtil.isLEZero(memberCacheService.getMemberTotalAward(memberCache))) {
            log.info("会员<{}:grade:{}>已获取奖金总数:{},总消费金额:{}, 出局", memberCache.getMemberId(),
                    memberCache.getMemberGrade(), totalAward, memberCache.getTotalConsumption());
            memberOutService.save(MemberOut.builder()
                    .memberId(memberCache.getMemberId())
                    .memberAccount(memberCache.getMemberAccount())
                    .memberGrade(memberCache.getMemberGrade())
                    .directAward(memberCache.getDirectAward())
                    .indirectAward(memberCache.getIndirectAward())
                    .communityAward(memberCache.getCommunityAward())
                    .nodeAward(memberCache.getNodeAward())
                    .totalConsumption(memberCache.getTotalConsumption())
                    .createTime(new Date())
                    .build());

            memberCache.setIndirectAward(BigDecimal.ZERO);
            memberCache.setDirectAward(BigDecimal.ZERO);
            memberCache.setCommunityAward(BigDecimal.ZERO);
            memberCache.setNodeAward(BigDecimal.ZERO);
            memberCache.setTotalConsumption(BigDecimal.ZERO);
            memberCache.setMemberGrade(GradeEnum.one.getValue());
            memberService.updateMemberGrade(memberCache.getMemberId(), GradeEnum.one.getValue());
            memberCacheService.setMemberCache(memberCache);
            return BigDecimal.ZERO;
        }

        return residueAwardLimit;
    }

}
